use axum::{
    extract::{ Path, State},
    extract::ConnectInfo,
    http::header,
    http::StatusCode,
    response::{Html, IntoResponse, Response},


};
use axum::extract::multipart::Multipart;
use std::{

    fs,
    path::{Component, PathBuf, MAIN_SEPARATOR},
    sync::Arc,

};
use tokio::time::{self, Duration};
use tokio::fs::File;
use std::fs::File as SyncFile;
use tokio::io::AsyncReadExt;
use serde::{Serialize};
use tera::{Tera, Context};
use log::{info, error};
use serde_json::json;
use std::io::Write;
use crate::{logdb::write_log_async, util::build_upload_url};
use crate::users::AuthSession;
use sqlx::{sqlite::SqlitePool, Error};

use axum::body::Body;
use serde::Deserialize;
use axum::Json;
use axum::extract::Query;
use std::collections::HashMap;
// 应用状态
pub struct AppState {
    pub tera: Tera,
}


// 初始化数据库连接池并创建日志表
pub async fn user_init_db() -> Result<SqlitePool, Error> {
    // 构建数据库路径（使用当前工作目录）
    let db_path = std::env::current_dir()?
        .join("users.db");
    
    // 确保目录存在
    if let Some(parent) = db_path.parent() {
        std::fs::create_dir_all(parent)?;
    }

    // 创建数据库连接池
    let db_url = db_path.to_str();

    let pool = SqlitePool::connect(db_url.expect("not-found")).await?;

    sqlx::query(
        "create table if not exists users
        (
            id integer primary key not null,
            username text not null unique,
            password text not null
        )",
    )
   .execute(&pool)
   .await?;
    Ok(pool)
}




async fn configer_uploadable(username: &str) -> Result<Option<i32>, sqlx::Error> {
    let db = user_init_db().await.expect("fail to init user db");
    sqlx::query_scalar::<_, i32>(
        "SELECT uploadperm FROM users WHERE username = $1 AND role = $2"
    )
    .bind(username)
    .bind("configer")
    .fetch_optional(&db)
    .await
}



/// 异步校验：username 必须是 configer，且 access_dir 等于或在其 accessdir 之下。
pub async fn is_user_uploadable(username: String) -> bool {
    match configer_uploadable(&username).await {
        Ok(Some(base)) => {
                            //println!("is_user_uploadable ? {}", base);
                            base == 1
        },
        Ok(None) | Err(_) => {
            //println!("is_user_uploadable ? {} lookup failed.", username);
            false
        },
    }
}



// 列出文件和目录
pub async fn list_files(
    ConnectInfo(addr): ConnectInfo<std::net::SocketAddr>,
    Path(subpath): Path<String>,
) -> Response {
    // 编译模板目录中的所有模板
    let tera = match Tera::new("templates/tera/**/*") {
        Ok(t) => t,
        Err(e) => {
            println!("模板编译错误: {}", e);
            std::process::exit(1);
        }
    };
    let state: Arc<AppState> = Arc::new(AppState {
        tera: tera,
    });
    info!("client {addr} list {subpath}");
    // crate::util::get_http_root_url()
    return list_by_path(State(state), Path(subpath)).await
}

// 下载文件
pub async fn download_file(
    Path(subpath): Path<String>,
    ConnectInfo(addr): ConnectInfo<std::net::SocketAddr>,
) -> Response {
    // 编译模板目录中的所有模板
    let tera = match Tera::new("templates/tera/**/*") {
        Ok(t) => t,
        Err(e) => {
            println!("模板编译错误: {}", e);
            std::process::exit(1);
        }
    };
    let state: Arc<AppState> = Arc::new(AppState {
        tera: tera,
    });
    // 构建完整路径
    let root_dir = crate::util::get_system_root();
    let full_path = match build_safe_path(&root_dir, &subpath) {
        Ok(path) => path,
        Err(e) => return render_error(&state.tera, StatusCode::INTERNAL_SERVER_ERROR, e.to_string()),
    };
    info!("someone {addr} want to download files: {}", full_path.display());
    // 检查路径是否存在
    if !full_path.exists() {
        return render_error(&state.tera, StatusCode::NOT_FOUND, "File not found".to_string());
    }

    // 检查是否是文件
    if !full_path.is_file() {
        return render_error(&state.tera, StatusCode::BAD_REQUEST, "Path is not a file".to_string());
    }

    // 读取文件内容
    let mut file = match File::open(&full_path).await {
        Ok(file) => file,
        Err(e) => return render_error(&state.tera, StatusCode::INTERNAL_SERVER_ERROR, e.to_string()),
    };

    let mut contents = Vec::new();
    if let Err(e) = file.read_to_end(&mut contents).await {
        return render_error(&state.tera, StatusCode::INTERNAL_SERVER_ERROR, e.to_string());
    }

    info!("files are downloading: {}", full_path.display());
    write_log_async( "admin".to_string(), "INFO".to_string(), addr.to_string(), "文件下载".to_string(),  full_path.clone().into_os_string().into_string().expect("路径非UTF-8格式"))
            .await.expect("Failed to write log");

    // 获取文件名
    let file_name = full_path.file_name()
        .and_then(|n| n.to_str())
        .unwrap_or("download.bin")
        .to_string();

    // 返回文件内容
    ([
        (axum::http::header::CONTENT_DISPOSITION, format!("attachment; filename=\"{}\"", file_name)),
        (axum::http::header::CONTENT_TYPE, "application/octet-stream".into()),
    ], contents).into_response()
}

// 构建安全路径
fn build_safe_path(root_dir: &PathBuf, subpath: &str) -> Result<PathBuf, String> {
    let mut full_path = root_dir.clone();
    
    // 处理空路径
    if subpath.is_empty() {
        return Ok(full_path);
    }
    //println!("full path {:?}", full_path.to_str());
    
    // 使用PathBuf::from来处理不同操作系统的路径分隔符
    let path = PathBuf::from(subpath);
    //println!("sub path {:?}", path.display().to_string());
        
    // 遍历路径组件并检查
    for component in path.components() {
        match component {
            Component::Normal(c) => {
                // 防止路径遍历攻击
                if c.to_str() == Some("..") {
                    return Err("Invalid path component".to_string());
                }
                full_path.push(c);
            }
            // 处理当前目录和父目录组件
            Component::CurDir => continue,
            Component::ParentDir => return Err("parent path invalid component".to_string()),
            // 处理根目录和前缀（如C:）
            Component::RootDir | Component::Prefix(_) => continue,
        }
    }
    
    // 确保路径在根目录内
    if !full_path.starts_with(root_dir) {
        info!(
            "Path {:?} is outside the root directory {:?}", 
            full_path.to_str(),
            root_dir.to_str()
        );
        return Err("Path is outside the root directory".to_string());
    }
    
    Ok(full_path)
}
// 获取父路径
fn get_parent_path(path: &str) -> String {
    if path.is_empty() {
        return "/".to_string();
    }
    
    let mut components: Vec<&str> = path.split(MAIN_SEPARATOR).collect();
    if components.is_empty() {
        return "/".to_string();
    }
    
    // 移除最后一个组件
    components.pop();
    
    // println!("{} parent path : {}", path, components.join(&MAIN_SEPARATOR.to_string()));
    if components.is_empty() {
        return "/".to_string();
    }
    // 重新组合路径
    
    // 将父路径转换为字符串
    let parent_str = components.join(&MAIN_SEPARATOR.to_string());
    
    // 确保路径以斜杠开头
    if !parent_str.starts_with('/') {
        format!("/{}", parent_str)
    } else {
        parent_str
    }
}

// 将时间转换为字符串
fn time_to_string(time: &std::time::SystemTime) -> String {
    use chrono::{DateTime, Local};
    
    let datetime: DateTime<Local> = time.clone().into();
    datetime.format("%Y-%m-%d %H:%M:%S").to_string()
}

// 渲染错误页面
fn render_error(tera: &Tera, status_code: StatusCode, message: String) -> Response {
    let mut context = Context::new();
    context.insert("status_code", &status_code.as_str());
    context.insert("message", &message);
    
    match tera.render("error.html", &context) {
        Ok(html) => (status_code, Html(html)).into_response(),
        Err(_) => (status_code, message).into_response(),
    }
}

// 文件信息结构
#[derive(Serialize)]
struct FileInfo {
    name: String,
    path: String,
    size: u64,
    modified: String,
    is_dir: bool,
}

// 处理文件上传请求
pub async fn handle_upload(     auth_session: AuthSession, 
                                ConnectInfo(addr): ConnectInfo<std::net::SocketAddr>,
                                mut multipart: Multipart) -> impl IntoResponse {

     // 编译模板目录中的所有模板
    let tera = match Tera::new("templates/tera/**/*") {
        Ok(t) => t,
        Err(e) => {
            println!("模板编译错误: {}", e);
            std::process::exit(1);
        }
    };
    let state: Arc<AppState> = Arc::new(AppState {
        // 初始化字段
        tera: tera,
    });

    let mut upload_success = false;
    let mut file_name = String::new();

    let updir = PathBuf::from(build_upload_url());
    println!("upload to dir {}", updir.display());
    let username = auth_session
        .user
        .as_ref()
        .map(|u| u.username.as_str())
        .unwrap_or_else(|| "");
    if !is_user_uploadable(username.to_string()).await {
        println!("upload by {} failed.", username);
        return Response::builder()
            .status(StatusCode::FORBIDDEN)
            .header(header::CONTENT_TYPE, "text/plain")
            .body(Body::from("没有上传文件的权限"))
            .unwrap();
    }    
    let mut upload_result_color = "gray";
    let mut upload_result_file = String::new();
    
    // 递归创建目录（如果不存在）
    fs::create_dir_all(&updir).unwrap();
    println!("created dir");
    // 设置合理的超时时间（示例：30秒）
    let timeout_duration = Duration::from_secs(30);
    loop {
        let timeout_result = time::timeout(timeout_duration, multipart.next_field()).await;
        println!("got field name");
        let field = match timeout_result {
            Ok(inner_result) => match inner_result {
                Ok(Some(field)) => field,
                Ok(None) => {
                    error!("没有更多表单字段");
                    break; // 退出循环
                }
                Err(e) => {
                    error!("解析表单字段失败: {}", e);
                    break; // 退出循环
                }
            },
            Err(_) => {
                error!("等待表单字段超时");
                break; // 退出循环
            }
        };

        let name = field.name().unwrap().to_string();
        println!("got file name");
        if name == "file" {
            file_name = updir.join(field.file_name().unwrap().to_string()).to_string_lossy().into_owned();
            let data = field.bytes().await.unwrap();
            println!("upload to {}", file_name);
            // 保存文件到本地
            let mut file = SyncFile::create(&file_name).expect("Failed to create file");
            if let Ok(_) = file.write_all(&data) {
                upload_success = true;
            }
        }
    }
    
    if upload_success {
        info!("Upload success: IP: {addr}, File: {}", file_name);
        upload_result_color = "green";
        upload_result_file = format!("文件上传 {} 成功", file_name);
    } else {
        error!("Upload failed: IP: unknown, File: {}", file_name);
        upload_result_color = "red";
        upload_result_file = format!("文件上传 {} 失败", file_name);
    }
    write_log_async( "admin".to_string(), "INFO".to_string(), addr.to_string(), "文件上传".to_string(),  upload_result_file.clone())
            .await.expect("Failed to write log");
    list_by_path_4upload(State(state), Path(build_upload_url()), upload_result_file, upload_result_color.to_string()).await

}


// 列出文件和目录
pub async fn list_by_path(
    State(state): State<Arc<AppState>>,
    Path(subpath): Path<String>,
) -> Response {
    list_by_path_4upload(State(state), Path(subpath), "".to_string(), "gray".to_string()).await
}



// 列出文件和目录
pub async fn list_by_path_4upload(
    State(state): State<Arc<AppState>>,
    Path(subpath): Path<String>,
    upload_file_name: String,
    upload_result_color: String,
) -> Response {
    // 构建完整路径
    let full_path = match build_safe_path(&crate::util::get_system_root(), &subpath) {
        Ok(path) => path,
        Err(e) => return render_error(&state.tera, StatusCode::INTERNAL_SERVER_ERROR, e.to_string()),
    };
    // info!("list files: {}", full_path.display());
    // 检查路径是否存在
    if !full_path.exists() {
        error!("not found: {}", full_path.display());
        return render_error(&state.tera, StatusCode::NOT_FOUND, "Path not found".to_string());
    }

    // 检查是否是目录
    if !full_path.is_dir() {
        return render_error(&state.tera, StatusCode::BAD_REQUEST, "Path is not a directory".to_string());
    }

    // 读取目录内容
    let entries = match fs::read_dir(&full_path) {
        Ok(entries) => entries,
        Err(e) => return render_error(&state.tera, StatusCode::INTERNAL_SERVER_ERROR, e.to_string()),
    };

    // 准备文件和目录列表
    let mut files = Vec::new();
    let mut directories = Vec::new();

    for entry in entries {
        let entry = match entry {
            Ok(entry) => entry,
            Err(e) => return render_error(&state.tera, StatusCode::INTERNAL_SERVER_ERROR, e.to_string()),
        };

        let file_name = entry.file_name().into_string().unwrap_or_else(|_| "invalid filename".to_string());
        let file_type = match entry.file_type() {
            Ok(ft) => ft,
            Err(e) => return render_error(&state.tera, StatusCode::INTERNAL_SERVER_ERROR, e.to_string()),
        };

        let mut file_info = FileInfo {
            name: file_name.clone(),
            path: format!("{}{}{}", subpath, MAIN_SEPARATOR, file_name),
            size: 0,
            modified: String::new(),
            is_dir: file_type.is_dir(),
        };

        // 处理目录
        if file_type.is_dir() {
            directories.push(file_info);
            continue;
        }

        // 处理文件
        if let Ok(metadata) = entry.metadata() {
            file_info.size = metadata.len();
            if let Ok(time) = metadata.modified() {
                file_info.modified = time_to_string(&time);
            }
            files.push(file_info);
        }
    }

    // 排序
    directories.sort_by_key(|d| d.name.to_lowercase());
    files.sort_by_key(|f| f.name.to_lowercase());

    let breadcrumbs = subpath.split('/')
        .filter(|p| !p.is_empty())
        .scan(Vec::new(), |path, part| {
            path.push(part.to_string());
            let obj = json!({
                "name": part,
                "url": path.join("/")
            });
            Some(obj)
        })
        .collect::<Vec<_>>();

    let qr_url = crate::util::get_qr_url();
    let qrcode_base64 = crate::util::generate_qr_from_config();
    // 准备模板上下文
    let mut context = Context::new();
    context.insert("directories", &directories);
    context.insert("files", &files);
    context.insert("current_path", &subpath);
    context.insert("parent_path", &get_parent_path(&subpath));
    context.insert("breadcrumbs", &breadcrumbs);
    context.insert("upload_result", &upload_file_name);
    context.insert("upload_result_color", &upload_result_color);
    context.insert("qrcode_base64", &qrcode_base64);
    context.insert("qr_url", &qr_url);
    //match env::current_dir() {
      //  Ok(path) => println!("当前工作目录: {}", path.display()),
      //  Err(e) => eprintln!("获取当前目录失败: {}", e),
    //};
    // 渲染模板
    match state.tera.render("index.html", &context) {
        Ok(html) => Html(html).into_response(),
        Err(e) => render_error(&state.tera, StatusCode::INTERNAL_SERVER_ERROR, e.to_string()),
    }
}


// 列出文件和目录
pub async fn tera_list_files(

    Path(subpath): Path<String>,
) -> Response {
    // 编译模板目录中的所有模板
    let tera = match Tera::new("templates/tera/**/*") {
        Ok(t) => t,
        Err(e) => {
            println!("模板编译错误: {}", e);
            std::process::exit(1);
        }
    };
    let state: Arc<AppState> = Arc::new(AppState {
        tera: tera,
    });
    return list_by_path(State(state), Path(subpath)).await
}


// ---------------------------
// 分片上传接口
// ---------------------------
#[derive(Debug, Deserialize)]
struct ChunkMeta {
    file_name: String,
    file_hash: String,
    chunk_index: usize,
    total_chunks: usize,
}

pub async fn upload_chunk(
    auth_session: AuthSession,
    ConnectInfo(addr): ConnectInfo<std::net::SocketAddr>,
    mut multipart: Multipart,
) -> impl IntoResponse {
     // 编译模板目录中的所有模板
    let tera = match Tera::new("templates/tera/**/*") {
        Ok(t) => t,
        Err(e) => {
            println!("模板编译错误: {}", e);
            std::process::exit(1);
        }
    };
    let state: Arc<AppState> = Arc::new(AppState {
        // 初始化字段
        tera: tera,
    });
    // 1. 权限校验（复用原有逻辑）
    let username = auth_session
        .user
        .as_ref()
        .map(|u| u.username.as_str())
        .unwrap_or_else(|| "");
    if !is_user_uploadable(username.to_string()).await {
        println!("upload by {} failed: 无权限", username);
        return render_error(
            &state.tera,
            StatusCode::FORBIDDEN,
            "没有上传文件的权限".to_string()
        );
    }

    // 2. 获取上传目录（复用原有 build_upload_url）
    let updir = PathBuf::from(build_upload_url());
    fs::create_dir_all(&updir).unwrap();  // 确保目录存在

    // 3. 解析 multipart 表单（分片元信息 + 分片数据）
    let mut meta = None;
    let mut chunk_data = None;

    //print!("正在解析上传数据...");
    while let Some(field) = multipart.next_field().await.unwrap() {
        match field.name() {
            Some("meta") => {
                // 解析分片元信息（JSON 格式）
                let meta_str = field.text().await.unwrap();
                meta = Some(serde_json::from_str::<ChunkMeta>(&meta_str).unwrap());
            }
            Some("chunk_data") => {
                // 获取分片二进制数据（替换原来的unwrap()）
                match field.bytes().await {
                    Ok(bytes) => {
                        chunk_data = Some(bytes);
                    }
                    Err(e) => {
                        error!(
                            "读取分片数据失败: IP: {addr}, 错误: {}",
                            e
                        );
                        // 返回错误响应
                        return render_error(
                            &state.tera,
                            StatusCode::BAD_REQUEST,
                            "分片数据读取失败，请重试".to_string()
                        );
                    }
                }
            }
            _ => (),
        }
    }

    // 校验分片信息完整性
    let (meta, chunk_data) = match (meta, chunk_data) {
        (Some(m), Some(d)) => (m, d),
        _ => {
            let error_msg = "分片信息或数据缺失".to_string();
            return render_error(&state.tera, StatusCode::BAD_REQUEST, error_msg);
        }
    };

    // 4. 保存分片到临时目录（临时目录基于文件哈希，避免冲突）
    let temp_chunk_dir = updir.join(".chunks").join(&meta.file_hash);  // 临时分片目录（放在上传目录下）
    println!("write to {}...", temp_chunk_dir.display());
    fs::create_dir_all(&temp_chunk_dir).unwrap();
    let chunk_path = temp_chunk_dir.join(format!("chunk_{}", meta.chunk_index));
    println!("write to {}...", chunk_path.display());
    let upload_success = match fs::write(&chunk_path, &chunk_data) {
        Ok(_) => {
            info!(
                "Chunk upload success: IP: {addr}, File: {}, Chunk: {}",
                meta.file_name, meta.chunk_index
            );
            println!("write to {} done", chunk_path.display());
            true
        }
        Err(e) => {
            error!(
                "Chunk upload failed: IP: {addr}, File: {}, Chunk: {}, Error: {}",
                meta.file_name, meta.chunk_index, e
            );
            println!("write to {} fail", chunk_path.display());
            false
        }
    };

    // 5. 准备上传结果信息（类似原有 upload 接口）
    let (upload_result_color, upload_result_file) = if upload_success {
        (
            "green".to_string(),
            format!(
                "分片 {} 上传成功（文件：{}，共 {} 片）",
                meta.chunk_index, meta.file_name, meta.total_chunks
            ),
        )
    } else {
        (
            "red".to_string(),
            format!(
                "分片 {} 上传失败（文件：{}）",
                meta.chunk_index, meta.file_name
            ),
        )
    };

    // 6. 记录日志（复用原有日志逻辑）
    write_log_async(
        "admin".to_string(),
        if upload_success { "INFO".to_string() } else { "ERROR".to_string() },
        addr.to_string(),
        "分片上传".to_string(),
        upload_result_file.clone(),
    )
    .await
    .expect("Failed to write log");

    // 7. 调用 list_by_path_4upload 返回文件列表页面（保持风格统一）
    list_by_path_4upload(
        State(state),
        Path(build_upload_url()),  // 传入当前上传目录
        upload_result_file,
        upload_result_color,
    )
    .await   // 1. 权限校验
}

// ---------------------------
// 分片合并接口
// ---------------------------
#[derive(Debug, Deserialize)]
pub struct MergeRequest {
    file_name: String,
    file_hash: String,
    total_chunks: usize,
}

pub async fn merge_chunks(
    auth_session: AuthSession,
    Json(req): Json<MergeRequest>,
) -> impl IntoResponse {
     // 编译模板目录中的所有模板
    let tera = match Tera::new("templates/tera/**/*") {
        Ok(t) => t,
        Err(e) => {
            println!("模板编译错误: {}", e);
            std::process::exit(1);
        }
    };
    let state: Arc<AppState> = Arc::new(AppState {
        // 初始化字段
        tera: tera,
    });
    // 1. 权限校验（复用原有逻辑）
    let username = auth_session
        .user
        .as_ref()
        .map(|u| u.username.as_str())
        .unwrap_or_else(|| "");
    if !is_user_uploadable(username.to_string()).await {
        println!("merge by {} failed: 无权限", username);
        return render_error(
            &state.tera,
            StatusCode::FORBIDDEN,
            "没有合并文件的权限".to_string()
        );
    }

    // 2. 构建路径（复用原有上传目录逻辑）
    let upload_dir = PathBuf::from(build_upload_url());  // 最终文件保存目录
    let temp_chunk_dir = upload_dir.join(".chunks").join(&req.file_hash);  // 临时分片目录（与 upload_chunk 对应）
    let target_path = upload_dir.join(&req.file_name);  // 合并后的目标文件路径

    // 3. 合并分片逻辑
    let mut merge_success = false;
    let mut error_msg = String::new();

    // 检查临时分片目录是否存在
    if !temp_chunk_dir.exists() {
        error_msg = "未找到分片文件，可能已被清理或从未上传".to_string();
    } else {
        // 创建目标文件
        match fs::File::create(&target_path) {
            Ok(mut target_file) => {
                // 按索引顺序合并所有分片
                for i in 0..req.total_chunks {
                    let chunk_path = temp_chunk_dir.join(format!("chunk_{}", i));
                    
                    let reading = format!("read {}...", chunk_path.display());
                    println!("read {}...", chunk_path.display());
                    // 读取分片内容
                    let chunk_data = match fs::read(&chunk_path) {
                        Ok(data) => data,
                        Err(e) => {
                            error_msg = format!("读取分片 {} 失败：{}", i, e);
                            break;
                        }
                    };

                    // 写入目标文件
                    if let Err(e) = target_file.write_all(&chunk_data) {
                        error_msg = format!("写入分片 {} 失败：{}", i, e);
                        break;
                    }

                    // 合并后删除单个分片（清理临时文件）
                    //let _ = fs::remove_file(chunk_path);
                }

                // 如果没有错误，标记合并成功并清理临时目录
                if error_msg.is_empty() {
                    merge_success = true;
                    let _ = fs::remove_dir_all(temp_chunk_dir);  // 删除分片目录
                }
            }
            Err(e) => {
                error_msg = format!("创建目标文件失败：{}", e);
            }
        }
    }

    // 4. 准备合并结果信息（与上传逻辑风格统一）
    let (upload_result_color, upload_result_file) = if merge_success {
        info!(
            "Merge success:File: {}",
            target_path.display()
        );
        (
            "green".to_string(),
            format!("文件 {} 合并成功（共 {} 个分片）", req.file_name, req.total_chunks),
        )
    } else {
        error!(
            "Merge failed: File: {}, Error: {}",
            req.file_name, error_msg
        );
        (
            "red".to_string(),
            format!("文件 {} 合并失败：{}", req.file_name, error_msg),
        )
    };


    // 6. 调用 list_by_path_4upload 返回文件列表页面（保持风格统一）
    list_by_path_4upload(
        State(state),
        Path(build_upload_url()),  // 传入上传目录路径
        upload_result_file,
        upload_result_color,
    )
    .await
}
// 后端新增：查询已上传的分片接口

pub async fn check_uploaded_chunks(
    auth_session: AuthSession,
    Query(params): Query<HashMap<String, String>>,
) -> impl IntoResponse {
     // 编译模板目录中的所有模板
    let tera = match Tera::new("templates/tera/**/*") {
        Ok(t) => t,
        Err(e) => {
            println!("模板编译错误: {}", e);
            std::process::exit(1);
        }
    };
    let state: Arc<AppState> = Arc::new(AppState {
        // 初始化字段
        tera: tera,
    });
    // 1. 权限校验
    let username = auth_session
        .user
        .as_ref()
        .map(|u| u.username.as_str())
        .unwrap_or_else(|| "");
    if !is_user_uploadable(username.to_string()).await {
        return Json(serde_json::json!({ "error": "无权限" })).into_response();
    }

    // 2. 获取文件哈希
    let file_hash = match params.get("file_hash") {
        Some(h) => h,
        None => return Json(serde_json::json!({ "error": "缺少file_hash" })).into_response(),
    };

    // 3. 读取临时分片目录，获取已上传的分片索引
    let upload_dir = PathBuf::from(build_upload_url());
    let temp_chunk_dir = upload_dir.join(".chunks").join(file_hash);
    let mut uploaded_chunks = Vec::new();

    if temp_chunk_dir.exists() {
        if let Ok(entries) = fs::read_dir(&temp_chunk_dir) {
            for entry in entries {
                if let Ok(entry) = entry {
                    let file_name = entry.file_name().into_string().unwrap_or_default();
                    // 匹配 "chunk_0" 格式的文件名，提取索引
                    if let Some(index_str) = file_name.strip_prefix("chunk_") {
                        if let Ok(index) = index_str.parse::<usize>() {
                            uploaded_chunks.push(index);
                        }
                    }
                }
            }
        }
    }
    println!("check uploaded_chunks {:?}", uploaded_chunks);
    // 4. 返回已上传的分片索引
    Json(serde_json::json!({
        "file_hash": file_hash,
        "uploaded_chunks": uploaded_chunks
    })).into_response()
}